#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

/*
建立套接字 scocket   返回sockfd
int socket(int domain, int type, int protocol);
    domain 网络协议 AF_INET    AF_INET6
    type  SOCK_STREAM 选择TCP   SOCK_DGRAM 选择UDP

绑定自己的IP地址  和 端口号   0、-1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    sockfd  套接字描述符
    addr    结构体sockaddr的指针 ip和端口
    addrlen  结构体的大小

listen()声明sockfd处于监听状态  0、-1
int listen(int sockfd, int backlog);
    sockfd ： 套接字文件描述符
    backlog  ：同时  最多支持连接上来的客户端总数


等待接受客户端连接  失败-1  返回已连接的套接字文件描述符
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    sockfd： 套接字文件描述符
    addr  ：  连接上来的客户端的IP地址和端口号
    addrlen ： 长度

接收数据  失败-1  返回0 表示客户端断开连接
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    sockfd： 已连接的套接字文件描述符  connectfd
    buf  ： 接收到的数据存储到这里
    len  ：  接收数据的大小
    flags  ：  一般设置为  0


int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
*/

void *start_routine(void *connectfd)
{

    printf("子线程 参数%d 线程号%lu\n", *((int *)connectfd), pthread_self());

    // 服务器向客户端发送数据
    while (1)
    {
        char buf[1024] = {0};
        printf("请输入向客户端发送的数据：");
        scanf("%s", buf);
        int send_res = send(*((int *)connectfd), buf, sizeof(buf), 0);
        if (send_res <= 0)
        {
            printf("send error\n");
            break;
        }
        printf("send ok... %d %s\n", send_res, buf);

        // 判断退出
        if (strcmp(buf, "exit") == 0)
        {
            break;
        }
    }

    printf("线程 end\n");
}

int main()
{
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error");
        return -1;
    }

    int optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    int sndbuf, rcvbuf;
    int len1 = sizeof(sndbuf);
    int len2 = sizeof(rcvbuf);
    getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sndbuf, &len1);
    getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len2);
    printf("sndbuf: %d, rcvbuf: %d\n", sndbuf, rcvbuf);

    printf("socket ok...\n");

    // 创建Server端地址结构体
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;                        // 使用ipv4
    addr.sin_port = htons(11111);                     // 端口   htons()  主机字节序转网络字节序host to net
    addr.sin_addr.s_addr = inet_addr("192.168.46.2"); // ip  inet_addr() 将主机IP 转成 网络IP
    // addr.sin_addr.s_addr = inet_addr("0.0.0.0"); // ip  inet_addr() 将主机IP 转成 网络IP
    // addr.sin_addr.s_addr = inet_addr("8.138.17.18"); // ip  inet_addr() 将主机IP 转成 网络IP
    // 0.0.0.0  表示本机所有网卡的ip

    // 绑定
    int bind_res = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
    if (bind_res < 0)
    {
        perror("bind error");
        return -1;
    }
    printf("bind ok...\n");

    // 监听
    int listen_res = listen(sockfd, 10);
    if (listen_res < 0)
    {
        perror("listen error");
        return -1;
    }
    printf("listen ok...\n");

    while (1)
    {
        printf("waiting...\n");
        // 创建Client端地址结构体
        struct sockaddr_in ClientAddr;
        memset(&addr, 0, sizeof(addr));

        // 接受客户端连接  返回已连接的套接字文件描述符
        int ClientAddr_len = sizeof(ClientAddr);
        int connectfd = accept(sockfd, (struct sockaddr *)&ClientAddr, &ClientAddr_len);
        if (connectfd < 0)
        {
            perror("accept error");
        }
        printf("Client Connected! ip: %s, port: %d\n", inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port));

        // =========
        pthread_t thread;
        int res = pthread_create(&thread, NULL, start_routine, &connectfd);
        printf("res=%d  thread=%lu\n", res, thread);

        // =========
        while (1)
        {
            // 接收数据
            char buf[1024] = {0};
            // recv() 一定用的是 connectfd
            ssize_t recv_res = recv(connectfd, buf, sizeof(buf), 0);
            if (recv_res < 0)
            {
                perror("recv error");
                continue;
            }
            printf("recv: %s\n", buf);
            if (recv_res == 0)
            {
                printf("client closed...\n");
                break;
            }

            // for (int i = 0; i < 10; i++)
            // {
            //     printf("%d-%c\n", buf[i], buf[i]);
            // }

            // 判断退出
            if (strcmp(buf, "exit") == 0)
            {
                break;
            }
        }

        // 服务端主动要求退出 ，取消接收来自客户端的消息1
        pthread_cancel(thread);
        // 阻塞等待线程结束
        pthread_join(thread, NULL);
        printf("回收线程\n");

        // 关闭已连接的套接字文件描述符
        close(connectfd);
        printf("server side close connectfd\n");
    }

    close(sockfd);
    return 0;
}

// https://newstar.wiki/learn/nc  tcp调试工具
// https://blog.csdn.net/qq_24305079/article/details/147476573 简易教程
// 模拟客户端  nc 192.168.46.2 11111
